//
// Created by 郝羽 on 2018/3/21.
//

#ifndef RTC_XSWITCH_PACKET_HPP
#define RTC_XSWITCH_PACKET_HPP

#include <pcap/pcap.h>
#include <memory.h>
#include <cstdint>
#include <memory.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <iostream>

inline uint16_t get2(const uint8_t *data, size_t i) { return (uint16_t)(data[i+1]) | ((uint16_t)(data[i]))<<8; }
inline void set2(uint8_t *data, size_t i, uint16_t val) {
	data[i+1] = (uint8_t)(val);
	data[i]   = (uint8_t)(val>>8);
}

class Packet {
    friend class Decoder;
public:
    // parse an ethernet/ip/udp packet
    static Packet* Parse(const pcap_pkthdr* hdr, const uint8_t* buf, int linktype) {
        int offset = 0;
        if (hdr->len != hdr->caplen) {
            fprintf(stderr, "could not parse packet: truncated\n");
        }

        if (linktype == DLT_EN10MB) {
            ether_header* eth = (ether_header*)buf;
            if (eth->ether_type != htons(ETHERTYPE_IP)) {
                fprintf(stderr, "could not parse packet: not ethernet/ip\n");
                return nullptr;
            }
            offset += sizeof(ether_header);
        } else if (linktype == DLT_NULL) {
            unsigned char proto = buf[0];
            switch (proto) {
                case 2: // ipv4
                case 24: // ipv6
                case 28: // ipv6
                case 30: // ipv6
                    offset += 4;
                    break;
            default:
                fprintf(stderr, "could not parse packet: not null/ip\n");
                    return nullptr;
            }
        }

        auto pkt = new Packet();
        ip* ih = (ip*)(buf + offset);
        // uint16_t lllne = get2(buf + offset + 2, 0);
        pkt->ip_len_ = (offset + 2);
        //fprintf(stderr, "%s -> %s \n", inet_ntoa(ih->ip_src), inet_ntoa(ih->ip_dst));
        if (ih->ip_p != IPPROTO_UDP) {
            fprintf(stderr, "could not parse packet: not udp\n");
            return nullptr;
        }
        offset += ih->ip_hl * 4;
        udphdr* udp = (udphdr*)(buf + offset);
        uint16_t ulllne = get2(buf + offset + 4, 0);
        // uint16_t len = get2((const uint8_t *)&(udp->len), 0);
        pkt->udp_len_ = (offset + 4);
        offset += sizeof(udphdr);

        auto v = *(buf + offset) >> 6;
        if (v != 2) {
            fprintf(stderr, "could not parse packet: not rtp\n");
            return nullptr;
        }

        bool isRtcp = false;
        auto pt = *(buf + offset + 1) & 0x7f;
        if (pt > 63 && pt < 96) {
            isRtcp = true;
        }

        pkt->pcapHeader_ = *hdr;
        pkt->capturedData_ = new uint8_t[hdr->caplen];
        memcpy(pkt->capturedData_, buf, hdr->caplen);
        pkt->src_.sin_addr = ih->ip_src;
        pkt->dst_.sin_addr = ih->ip_dst;
#ifdef __APPLE__
        pkt->src_.sin_port = udp->uh_sport;
        pkt->dst_.sin_port = udp->uh_dport;
#else
        pkt->src_.sin_port = udp->source;
        pkt->dst_.sin_port = udp->dest;
#endif
        pkt->rtpOffset_ = offset;
        pkt->isRtcp_ = isRtcp;

        return pkt;
    }

    pcap_pkthdr pcapHeader_;
    uint8_t*    capturedData_;
    sockaddr_in src_;
    sockaddr_in dst_;
    uint        ip_len_;
    uint        udp_len_;
    int         rtpOffset_;
    bool        isRtcp_;
};

#endif //RTC_XSWITCH_PACKET_HPP
